package com.habu.oss.springboot.starter;

import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.util.StringUtils;
import software.amazon.awssdk.core.internal.util.Mimetype;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;

import java.io.InputStream;
import java.nio.file.Path;
import java.time.Duration;
import java.util.List;

/**
 * @Author 公众号：码猿技术专栏
 * @Description OssTemplate的实现类
 * @ClassName OssTemplateImpl
 * @Date 2023/3/18 19:02
 **/
@RequiredArgsConstructor
public class OssTemplateImpl implements OssTemplate {

    private final S3Client client;
    private final S3Presigner presigner;
    private final OssProperties properties;

    /**
     * 判断bucket是否存在
     * @param bucket bucket名称
     */
    @Override
    @SneakyThrows
    public boolean bucketExist(String bucket) {
        try {
            // bucket不存在则新增，注意云服务器厂商的bucket是全局唯一的
            client.headBucket(r -> r.bucket(bucket));
            return true;
        } catch (NoSuchBucketException e) {
            return false;
        }
    }

    /**
     * 创建Bucket
     * @param bucket bucket名称
     */
    @Override
    @SneakyThrows
    public void createBucket(String bucket, BucketCannedACL acl) {
        if (!bucketExist(bucket)) {
            // 不同厂商的acl策略设置不一样，最好提前建好bucket
            client.createBucket(r ->r.bucket(bucket).acl(acl));
        }
    }

    /**
     * 获取所有的buckets
     * @return
     */
    @Override
    @SneakyThrows
    public List<Bucket> getAllBuckets() {
        return client.listBuckets().buckets();
    }

    /**
     * 通过Bucket名称删除Bucket
     * @param bucket
     */
    @Override
    @SneakyThrows
    public void removeBucket(String bucket) {
        client.deleteBucket(r -> r.bucket(bucket));
    }

    /**
     * 上传对象
     * @param bucket bucket名称
     * @param objectName 文件名称
     * @param stream 文件流
     * @param contextType 文件类型
     */
    @Override
    @SneakyThrows
    public void putObject(String bucket, String objectName, InputStream stream, String contextType) {
        putObject(bucket, objectName, stream, stream.available(), contextType);
    }

    /**
     * 上传对象
     * @param bucket bucket名称
     * @param objectName 文件名称
     * @param stream 文件流
     */
    @Override
    @SneakyThrows
    public void putObject(String bucket, String objectName, InputStream stream) {
        putObject(bucket, objectName, stream, stream.available(), Mimetype.getInstance().getMimetype(Path.of(objectName)));
    }

    /**
     * 通过bucket和objectName获取对象
     * @param bucket bucket名称
     * @param objectName 文件名称
     */
    @Override
    @SneakyThrows
    public GetObjectResponse getObject(String bucket, String objectName) {
        return client.getObject(r -> r.bucket(bucket).key(objectName)).response();
    }

    /**
     * 获取对象的签名url
     * @param bucket
     * @param objectName
     * @param duration
     * @return
     */
    @Override
    @SneakyThrows
    public String getExpiresObjectURL(String bucket, String objectName, Duration duration) {
        final PresignedGetObjectRequest presignedGetObjectRequest = presigner
                .presignGetObject(r -> r.getObjectRequest(builder -> builder.bucket(bucket).key(objectName))
                        .signatureDuration(duration)
                );
        return presignedGetObjectRequest.url().toString();
    }

    /**
     * 获取对象的限时url
     * @param bucket
     * @param objectName
     * @return
     */
    @Override
    @SneakyThrows
    public String getPublicObjectURL(String bucket, String objectName) {
        // 配置了域名的优先使用域名作为路径
        if (StringUtils.hasText(properties.getDomain())) {
            return String.format("%s/%s", properties.getDomain(), objectName);
        }

        return client.utilities().getUrl(
                r -> r.bucket(bucket).key(objectName)).toString();
    }

    /**
     * 通过bucket和objectName删除对象
     * @param bucket
     * @param objectName
     */
    @Override
    @SneakyThrows
    public void removeObject(String bucket, String objectName) {
        client.deleteObject(r -> r.bucket(bucket).key(objectName));
    }

    /**
     * 根据bucket和prefix获取对象集合
     *
     * @param bucket bucket名称
     * @param prefix     前缀
     * @param recursive  是否递归查询
     */
    @Override
    @SneakyThrows
    public List<S3Object> getAllObjectsByPrefix(String bucket, String prefix, boolean recursive) {
        return client.listObjects(r -> r.bucket(bucket).prefix(prefix)).contents();
    }

    /**
     * 上传对象
     * @param bucket
     * @param objectName
     * @param stream
     * @param size
     * @param contextType
     * @return
     */
    @SneakyThrows
    private PutObjectResponse putObject(String bucket, String objectName, InputStream stream, long size
            , String contextType)  {
        return client.putObject(b -> b.bucket(bucket).key(objectName).contentType(contextType),
                RequestBody.fromInputStream(stream, size));

    }
}
